package com.zone.sdk.isd.util;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class ImageBmpUtil {
    private static final String TAG = ImageBmpUtil.class.getSimpleName();
    private static final int BMP_WIDTH_OF_TIMES = 4;
    private static final int BYTE_PER_PIXEL = 3;

    public static byte[] saveBitmap(Context context, Bitmap bitmap, String filePath, String filePathPlay, int width, int height, int quality, Bitmap template) {
        byte[] result;
        int bitmapWidth = bitmap.getWidth();
        int bitmapHeight = bitmap.getHeight();
        File defaultCacheFile = ImageUtil.getFileToCacheDirectory(context, bitmap, filePath);
       if (bitmapWidth == width && bitmapHeight == height){
            result = saveBmp(bitmap, filePathPlay, template);
        }else {
            String defaultFilePath = defaultCacheFile.getAbsolutePath();
            String compressFilePath = ImageBmpUtil.compress(context, defaultFilePath, filePathPlay, width, height, quality);
            Bitmap compressBitmap = BitmapFactory.decodeFile(compressFilePath);
            result = saveBmp(compressBitmap, filePathPlay, template);
        }
        return result;
    }

    /**
     * 指定分辨率图片压缩
     * @param context
     * @param filePath
     * @param width
     * @param height
     * @param quality
     * @return
     */
    public static String compress(Context context, String filePath, String fileName, int width, int height, int quality){
        String compressFilePath = FileUtil.getDefaultCacheDirectory(context) + "/compress_"+System.currentTimeMillis()+".png";
        ImageUtil.transImage(filePath, compressFilePath, width, height, quality);
        return compressFilePath;
    }

    /**
     * 生成ISD投放图片码流
     * @param bitmap
     * @param filePath
     * @param template
     * @return
     */
    private static byte[] saveBmp(Bitmap bitmap, String filePath, Bitmap template) {

//        byte[] result = null;

        if (bitmap == null) {
            return null;
        }

        if (filePath == null || "".equals(filePath)) {
            return null;
        }

        //image size
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        int[] templateColor = null;

        boolean isFront = true;
        if (template.getWidth() == 365){
            isFront = false;
        }

        if (template != null){
            templateColor = new int[template.getWidth() * template.getHeight()];
            template.getPixels(templateColor, 0, template.getWidth(), 0, 0, template.getWidth(), template.getHeight());
        }
        //image dummy data size
        //reason : bmp file's width equals 4's multiple
        int dummySize = 0;
        byte[] dummyBytesPerRow = null;
        boolean hasDummy = false;
        if (isBmpWidth4Times(width)) {
            hasDummy = true;
            dummySize = BMP_WIDTH_OF_TIMES - (width * BYTE_PER_PIXEL % BMP_WIDTH_OF_TIMES);
            dummyBytesPerRow = new byte[dummySize * height];
//            for (int i = 0; i < dummyBytesPerRow.length; i++) {
//                dummyBytesPerRow[i] = (byte) 0x00;
//            }
        }

        int[] pixels = new int[width * height];
        int imageSize = pixels.length * BYTE_PER_PIXEL;
        if (dummyBytesPerRow != null){
            imageSize += dummyBytesPerRow.length;
        }
//        result = new byte[imageSize];

        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

        int imageDataOffset = 0x36;
        int fileSize = imageSize + imageDataOffset;

        ByteBuffer buffer = ByteBuffer.allocate(fileSize);
        ByteBuffer bufferBody = ByteBuffer.allocate(imageSize);

        try {
            fillHead(buffer, fileSize, imageDataOffset, width, height, imageSize);



            for (int y = height-1; y >= 0; y--){
                for (int x=0; x < width; x++){
                    if (templateColor != null){
                        buffer.put(write24BitForPixcelISD(ISDColorUtil.getIsdPlayColor(templateColor[y*width+x], pixels[y*width+x], isFront)));
                        bufferBody.put(write24BitForPixcelISD(ISDColorUtil.getIsdPlayColor(templateColor[y*width+x], pixels[y*width+x], isFront)));
                    }else {
                        buffer.put(write24BitForPixcelISD(pixels[y*width+x]));
                        bufferBody.put(write24BitForPixcelISD(pixels[y*width+x]));
                    }
                }
                if (hasDummy) {
                    buffer.put(dummyBytesPerRow[y]);
                    bufferBody.put(dummyBytesPerRow[y]);
                }
            }

            FileOutputStream fos = new FileOutputStream(filePath);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
//            byte[] outBytes = buffer.array();
//            System.arraycopy(outBytes, imageDataOffset-1, result, 0, result.length);
            fos.write(buffer.array());
            bos.flush();
            fos.close();
            bos.close();

        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return bufferBody.array();
    }

    /**
     * 添加图片头
     * @param buffer
     * @param fileSize
     * @param imageDataOffset
     * @param width
     * @param height
     * @param imageSize
     * @throws IOException
     */
    private static void fillHead(ByteBuffer buffer, int fileSize, int imageDataOffset, int width, int height, int imageSize) throws IOException {
        /**
         * BITMAP FILE HEADER Write Start
         **/
        buffer.put((byte) 0x42);
        buffer.put((byte) 0x4D);

        //size
        buffer.put(writeInt(fileSize));

        //reserved
        buffer.put(writeShort((short) 0));
        buffer.put(writeShort((short) 0));

        //image data start offset
        buffer.put(writeInt(imageDataOffset));

        /** BITMAP FILE HEADER Write End */

        //*******************************************

        /** BITMAP INFO HEADER Write Start */
        //size
        buffer.put(writeInt(0x28));

        //width, height
        buffer.put(writeInt(width));
        buffer.put(writeInt(height));

        //planes
        buffer.put(writeShort((short) 1));

        //bit count
        buffer.put(writeShort((short) 24));

        //bit compression
        buffer.put(writeInt(0));

        //image data size
        buffer.put(writeInt(imageSize));

        //horizontal resolution in pixels per meter
        buffer.put(writeInt(0));

        //vertical resolution in pixels per meter (unreliable)
        buffer.put(writeInt(0));

        buffer.put(writeInt(0));

        buffer.put(writeInt(0));

        /** BITMAP INFO HEADER Write End */
    }

    /**
     * Is last pixel in Android Bitmap width
     *
     * @param width
     * @param i
     * @return
     */
    private static boolean isBitmapWidthLastPixcel(int width, int i) {
        return i > 0 && (i % (width - 1)) == 0;
    }

    /**
     * BMP file is a multiples of 4?
     *
     * @param width
     * @return
     */
    private static boolean isBmpWidth4Times(int width) {
        return width % BMP_WIDTH_OF_TIMES > 0;
    }

    /**
     * Write integer to little-endian
     *
     * @param value
     * @return
     * @throws IOException
     */
    private static byte[] writeInt(int value) throws IOException {
        byte[] b = new byte[4];

        b[0] = (byte) (value & 0x000000FF);
        b[1] = (byte) ((value & 0x0000FF00) >> 8);
        b[2] = (byte) ((value & 0x00FF0000) >> 16);
        b[3] = (byte) ((value & 0xFF000000) >> 24);

        return b;
    }

    /**
     * Write integer pixel to little-endian byte array
     *
     * @param value
     * @return
     * @throws IOException
     */
    private static byte[] write24BitForPixcelISD(int value) throws IOException {
//        if (value != -16777216){
//            int red = ColorUtil.getRed(value);
//            int green = ColorUtil.getGreen(value);
//            int blue = ColorUtil.getBlue(value);
//            if (blue > red && blue > green){
//                red = blue;
//                green = blue;
//            }else if (green > red && green > blue){
//                red = green;
//                blue = 0;
//            }else if (red > green && red > blue){
//                green = 0;
//                blue = 0;
//            }
//            value = ColorUtil.getColor(red, green, blue);
//        }
        byte[] b = new byte[3];
        b[0] = (byte) (value & 0x000000FF); //B
        b[1] = (byte) ((value & 0x0000FF00) >> 8); //G
        b[2] = (byte) ((value & 0x00FF0000) >> 16); //R

//            if (b[2] > b[1]){
//                if (b[0] > b[2]){
//                    //b0 最大 为红色系，不变
//                }else {
//                    //b2 最大 为蓝色，变为白色系
//                    b[0] = b[2];
//                    b[1] = b[2];
//                }
//            }else {
//                if (b[1] > b[2]){
//                    //b1 最大 为绿色系 变为黄色系
//                    b[0] = b[1];
//                    b[2] = b[1];
//                }else {
//                    //b2 最大 为蓝色，变为白色系
//                    b[0] = b[2];
//                    b[1] = b[2];
//                }
//            }
//        }
        return b;
    }

    /**
     * Write integer pixel to little-endian byte array
     *
     * @param value
     * @return
     * @throws IOException
     */
    private static byte[] write24BitForPixcel(int value) throws IOException {
        byte[] b = new byte[3];
        b[0] = (byte) (value & 0x000000FF);
        b[1] = (byte) ((value & 0x0000FF00) >> 8);
        b[2] = (byte) ((value & 0x00FF0000) >> 16);

        return b;
    }

    /**
     * Write short to little-endian byte array
     *
     * @param value
     * @return
     * @throws IOException
     */
    private static byte[] writeShort(short value) throws IOException {
        byte[] b = new byte[2];

        b[0] = (byte) (value & 0x00FF);
        b[1] = (byte) ((value & 0xFF00) >> 8);

        return b;
    }
}
